package flexo.graphic;

import java.util.LinkedList;
import java.util.List;

import org.lwjgl.*;
import org.lwjgl.opengl.*;

import flexo.*;
import flexo.common.LogMsgType;
import flexo.math.Vector;

public class Window
{
	Vector displayMode;
	List <FrameListener> frameListeners;
	
	public Window(int width, int height, String windowTitle, boolean fullScreen)
	{
		init(width, height, windowTitle,fullScreen);
	}
	
	private void init(int width, int height, String windowTitle, boolean fullScreen)
	{
		try 
		{
			DisplayMode[] modes = Display.getAvailableDisplayModes();
			DisplayMode chosenMode = null;

			for (int i=0;i<modes.length;i++) 
			{
				if ((modes[i].getWidth() == width) && (modes[i].getHeight() == height))
				{
					chosenMode = modes[i];
					break;
				}
			}
			
			flexo.Kernel.log("flexo.graphic.Window", "Initializing window (" + width + "x" + height + ")");
			
			Display.setDisplayMode(chosenMode);
			Display.setTitle(windowTitle);
			Display.setFullscreen(fullScreen);
			Display.create();	
			Display.sync(flexo.common.System.UPDATE_MAX);
			
			displayMode = new Vector(chosenMode.getWidth(),chosenMode.getHeight(),chosenMode.getBitsPerPixel());
			
			// Test VBO support
			flexo.Kernel.log("flexo.graphic.Window", "Checking graphic capabilities");
			if ( !GLContext.getCapabilities().GL_ARB_vertex_buffer_object ) 
			{
				flexo.Kernel.log("flexo.graphic.Window", "ARB VBO extension not supported! (and needed by the engine)", LogMsgType.ERROR);
				flexo.Kernel.error();
			}
			
			// Disable lighting
			// If you want to use lights, code them in a shader
			GL11.glDisable(GL11.GL_LIGHTING);
			
			// Smooth shading by default
			GL11.glShadeModel(GL11.GL_SMOOTH);
			
			// Enable texture mapping
			GL11.glEnable(GL11.GL_TEXTURE_2D);
			// Depth Buffer Setup
			GL11.glClearDepth(1.0f);			
			GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
			
			GL11.glTexParameterf( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT );
			GL11.glTexParameterf( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT );
			
			frameListeners = new LinkedList<FrameListener>();
		} 
		catch (LWJGLException e)
		{
			
			flexo.Kernel.log("flexo.graphic.Window", "Unable to initialize window: " + e.getMessage(), LogMsgType.ERROR);
			Kernel.error();
		}
	}
	
	public void render(List<Camera> cameraList)
	{	
		// Window clear //////////////////////////////////////
		GL11.glClearColor(0,0,0,1);
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		
		for (FrameListener f : frameListeners)
		{
			f.onFrameStart();
		}
		
		for (Camera cam: cameraList)
		{
			// Camera setup //////////////////////////////////	
			cam.update();
			
			for (FrameListener f : frameListeners)
			{
				f.onCameraRender();
			}
						
			// Rendering objects /////////////////////////////
			cam.render();
		}
		
		// Flip buffers //////////////////////////////////////
	
		// Updating the display
		Display.update();
		
		// Handling the windows close button
		if (Display.isCloseRequested())
		{
			flexo.Kernel.release();
		}
	}
	
	void release()
	{
		Display.destroy();
		flexo.Kernel.log("flexo.graphic.Window", "Window closed");
	}
	
	public Vector getDisplayMode()
	{
		return displayMode;
	}
	
	/**
	 * @return An array of vectors containing the display modes.
	 * X stores the width, Y the height and Z the bits of depth.
	 */
	public static Vector[] getDisplayModes()
	{
		try
		{
			DisplayMode[] dm = Display.getAvailableDisplayModes();
			Vector[] vm = new Vector[dm.length];
			
			for (int i = 0; i < dm.length; i++)
			{
				vm[i] = new Vector(dm[i].getWidth(),dm[i].getHeight(),dm[i].getBitsPerPixel());
			}
			return vm;
		} 
		catch (LWJGLException e)
		{
			flexo.Kernel.log("flexo.graphic.Window", "Unable to determine display modes", LogMsgType.ERROR);
			flexo.Kernel.error();
			return null;
		}		
	}
}